package cn.buk.tms.dao;

import cn.buk.common.sc.CommonSearchCriteria;
import cn.buk.tms.dto.AuthInfo;
import cn.buk.tms.dto.FlightOrderPassengerDto;
import cn.buk.tms.dto.FlightOrderStatusStat;
import cn.buk.tms.entity.base.BaseOrderInfo_;
import cn.buk.tms.entity.order.OrderAttachment;
import cn.buk.tms.factory.OperationLogFactory;
import cn.buk.tms.sc.SearchFlightOrderCriteria;
import cn.buk.tms.common.constant.TmsOrderConstant;
import cn.buk.tms.entity.misc.*;
import cn.buk.tms.entity.order.flight.*;
import cn.buk.tms.flight.request.FlightOrderRequestDto;
import cn.buk.common.util.DateUtil;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.criteria.*;
import jakarta.transaction.Transactional;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import static cn.buk.common.Constant.*;
import static cn.buk.tms.common.constant.TmsOrderConstant.*;

/**
 * @author yfdai
 */
@Component
@Transactional
public class FlightOrderDaoImpl extends AbstractDao implements FlightOrderDao {

  private static final Logger logger = LogManager.getLogger(FlightOrderDaoImpl.class);

  @Override
  public int createOrder(FlightOrder o, final String username) {
    if (o.getPnrDetail() != null && o.getPnrDetail().trim().length() > 2000) {
      logger.info("pnr detail length: " + o.getPnrDetail().trim().length());
      o.setPnrDetail(o.getPnrDetail());
    }

    em.persist(o);

    OperationLog log = createFlightOrderLog(o.getEnterpriseId(), o.getId(), username, "创建订单");
    em.persist(log);

    return o.getId();
  }


  @Override
  public int updatePrice(final int enterpriseId, final String username, final int orderId, final FlightOrderRequestDto dto) {

    var flightOrder = em.find(FlightOrder.class, orderId);

    if (dto.getPsgCount() == 0) {
      dto.setPsgCount(dto.getAdultCount() + dto.getChildCount());
    }
    dto.calcCost();

    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.priceChecked = 1, " +
                    //成人价格
                    "o.price = :price, " +
                    "o.parvalue = :parvalue, " +
                    "o.tax = :tax, " +
                    "o.insurance = :insurance, " +
                    "o.serviceCharge = :serviceCharge, " +
                    "o.commission = :commission, " +
                    "o.commission2 = :commission2, " +
                    "o.discount = :discount, " +
                    "o.amount = :amount, " +
                    "o.cost = :cost, " +
                    //儿童价格
                    "o.chdPrice = :chdPrice, " +
                    "o.chdParvalue = :chdParvalue, " +
                    "o.chdTax = :chdTax, " +
                    "o.chdInsurance = :chdInsurance, " +
                    "o.chdServiceCharge = :chdServiceCharge, " +
                    "o.chdCommission = :chdCommission, " +
                    "o.chdCommission2 = :chdCommission2, " +
                    "o.chdDiscount = :chdDiscount, " +
                    "o.chdAmount = :chdAmount, " +
                    "o.chdCost = :chdCost, " +
                    //婴儿价格
                    "o.infPrice = :infPrice, " +
                    "o.infParvalue = :infParvalue, " +
                    "o.infTax = :infTax, " +
                    "o.infInsurance = :infInsurance, " +
                    "o.infServiceCharge = :infServiceCharge, " +
                    "o.infCommission = :infCommission, " +
                    "o.infCommission2 = :infCommission2, " +
                    "o.infDiscount = :infDiscount, " +
                    "o.infAmount = :infAmount, " +
                    "o.infCost = :infCost, " +
                    //总价格
                    "o.totalAmount = :totalAmount, " +
                    "o.totalCost = :totalCost, " +
                    "o.profit = :profit, o.lastUpdate = :lastUpdate " +
                    "where o.id = :orderId " +
                    "and o.enterpriseId = :enterpriseId " +
                    "and o.status <> :status")
            .setParameter("orderId", orderId)
            .setParameter("enterpriseId", enterpriseId)
            //成人
            .setParameter("price", dto.getAdultPrice().getPrice())
            .setParameter("parvalue", dto.getAdultPrice().getParValue())
            .setParameter("tax", dto.getAdultPrice().getTax())
            .setParameter("insurance", dto.getAdultPrice().getInsurance())
            .setParameter("serviceCharge", dto.getAdultPrice().getServiceCharge())
            .setParameter("commission", dto.getAdultPrice().getCommission())
            .setParameter("commission2", dto.getAdultPrice().getCommission2())
            .setParameter("discount", dto.getAdultPrice().getDiscount())
            .setParameter("amount", dto.getAdultPrice().getAmount())
            .setParameter("cost", dto.getAdultPrice().getCost())
            //儿童
            .setParameter("chdPrice", dto.getChildPrice().getPrice())
            .setParameter("chdParvalue", dto.getChildPrice().getParValue())
            .setParameter("chdTax", dto.getChildPrice().getTax())
            .setParameter("chdInsurance", dto.getChildPrice().getInsurance())
            .setParameter("chdServiceCharge", dto.getChildPrice().getServiceCharge())
            .setParameter("chdCommission", dto.getChildPrice().getCommission())
            .setParameter("chdCommission2", dto.getChildPrice().getCommission2())
            .setParameter("chdDiscount", dto.getChildPrice().getDiscount())
            .setParameter("chdAmount", dto.getChildPrice().getAmount())
            .setParameter("chdCost", dto.getChildPrice().getCost())
            //婴儿
            .setParameter("infPrice", dto.getInfantPrice().getPrice())
            .setParameter("infParvalue", dto.getInfantPrice().getParValue())
            .setParameter("infTax", dto.getInfantPrice().getTax())
            .setParameter("infInsurance", dto.getInfantPrice().getInsurance())
            .setParameter("infServiceCharge", dto.getInfantPrice().getServiceCharge())
            .setParameter("infCommission", dto.getInfantPrice().getCommission())
            .setParameter("infCommission2", dto.getInfantPrice().getCommission2())
            .setParameter("infDiscount", dto.getInfantPrice().getDiscount())
            .setParameter("infAmount", dto.getInfantPrice().getAmount())
            .setParameter("infCost", dto.getInfantPrice().getCost())
            //总的
            .setParameter("totalAmount", dto.getTotalAmount())
            .setParameter("totalCost", dto.getTotalCost())
            .setParameter("profit", dto.getTotalProfit())
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("updatePrice failed.");
    }

    StringBuilder sb = new StringBuilder();
    sb.append("修改价格(");
    if (flightOrder.getPrice() != dto.getAdultPrice().getPrice()) {
      sb.append("票面价：").append(flightOrder.getPrice()).append("->").append(dto.getAdultPrice().getPrice()).append(", ");
    }
    if (flightOrder.getParvalue() != dto.getAdultPrice().getParValue()) {
      sb.append("销售价：").append(flightOrder.getParvalue()).append("->").append(dto.getAdultPrice().getParValue()).append(", ");
    }

    if (flightOrder.getTax() != dto.getAdultPrice().getTax()) {
      sb.append("税：").append(flightOrder.getTax()).append("->").append(dto.getAdultPrice().getTax()).append(", ");
    }

    if (flightOrder.getCommission() != dto.getAdultPrice().getCommission()) {
      sb.append("代理费：").append(flightOrder.getCommission()).append("->").append(dto.getAdultPrice().getCommission()).append(", ");
    }

    if (flightOrder.getCommission2() != dto.getAdultPrice().getCommission2()) {
      sb.append("代理费2：").append(flightOrder.getCommission2()).append("->").append(dto.getAdultPrice().getCommission2()).append(", ");
    }

    if (flightOrder.getServiceCharge() != dto.getAdultPrice().getServiceCharge()) {
      sb.append("服务费：").append(flightOrder.getServiceCharge()).append("->").append(dto.getAdultPrice().getServiceCharge()).append(", ");
    }

    if (flightOrder.getDiscount() != dto.getAdultPrice().getDiscount()) {
      sb.append("优惠：").append(flightOrder.getDiscount()).append("->").append(dto.getAdultPrice().getDiscount()).append(", ");
    }

    if (flightOrder.getTotalAmount() != dto.getTotalAmount()) {
      sb.append("总金额：").append(flightOrder.getTotalAmount()).append("->").append(dto.getTotalAmount()).append(", ");
    }

    if (flightOrder.getTotalCost() != dto.getTotalCost()) {
      sb.append("总成本：").append(flightOrder.getTotalCost()).append("->").append(dto.getTotalCost()).append(", ");
    }

    if (flightOrder.getProfit() != dto.getTotalProfit()) {
      sb.append("总利润：").append(flightOrder.getProfit()).append("->").append(dto.getTotalProfit()).append(", ");
    }

    sb.append("已校验价格).");

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, sb.toString());
    em.persist(log);

    return retCode;
  }

  @Override
  public int updatePassenger(AuthInfo authInfo, FlightOrderPassengerDto psgDto) {
    int retCode = em.createQuery("update FlightOrderPassenger o set " +
                    "o.name = :name, o.idNo = :idNo, o.ffpNo = :ffpNo, o.mobile = :mobile," +
                    "o.birthday = :birthday, o.psgType = :psgType, o.gender = :gender " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.order.id = :orderId")
            .setParameter("id", psgDto.getPsgId())
            .setParameter("enterpriseId", authInfo.getEnterpriseId())
            .setParameter("orderId", psgDto.getOrderId())
            .setParameter("name", psgDto.getName())
            .setParameter("idNo", psgDto.getIdNo())
            .setParameter("ffpNo", psgDto.getFfpNo())
            .setParameter("mobile", psgDto.getMobile())
            .setParameter("birthday", psgDto.getBirthday())
            .setParameter("psgType", psgDto.getPsgType())
            .setParameter("gender", psgDto.getGender())
            .executeUpdate();

    if (retCode == 1) {
      var log = createFlightOrderLog(authInfo.getEnterpriseId(), psgDto.getOrderId(), authInfo.getUsername(),
              "修改乘机人(" + psgDto.getName() + ")资料.");
      em.persist(log);
    }

    return retCode;
  }


  @Override
  public int updatePnr(int enterpriseId, String username, int orderId, String pnrNo) {
    int retCode = 0;

    FlightOrder flightOrder = em.find(FlightOrder.class, orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("FlightOrder is not existed.");
    }

    String oldPnrNo = flightOrder.getPnrNo();

    retCode = em.createQuery("update FlightOrder o set " +
                    "o.pnrNo = :pnr, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status <> :status")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("pnr", pnrNo)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .executeUpdate();

    if (retCode == 1) {
      String remark = "修改编码" + (oldPnrNo == null ? " " : oldPnrNo) + "为" + pnrNo + ".";
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public void updatePnrDetail(final int enterpriseId, String username, final int orderId, String pnrDetail) {
    FlightOrder flightOrder = em.find(FlightOrder.class, orderId);
    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      return;
    }
    em.refresh(flightOrder);

    if (flightOrder.getStatus() == FLIGHT_ORDER_STATUS_FINISHED) {
      flightOrder.setPnrRefreshStatus(1);
    }

    flightOrder.setPnrDetail(pnrDetail);
    flightOrder.setLastUpdate(DateUtil.getCurDateTime());

    em.merge(flightOrder);

    final String remark = "更新编码内容(" + flightOrder.getPnrRefreshStatus() + ", " + flightOrder.getStatus() + ")";
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

  }

  @Override
  public void updateFlightOrderRefreshPnrStatusFailed(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.pnrRefreshStatus = 4,  o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

  }

  @Override
  public void updateFlightOrderDetrStatus(int enterpriseId, String username, int orderId, int detrStatus, String remark) {
    int retCode = em.createQuery("update FlightOrder o set o.detrRefreshStatus = :detrStatus, " +
                    "o.detrRefreshTime = :lastUpdate, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("detrStatus", detrStatus)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "DETR更新(" + detrStatus + ", " + remark + ".");
      em.persist(log);
    }

  }

  @Override
  public void updateFlightOrderDetrStatus2(int enterpriseId, String username, int orderId, int detrStatus, String remark) {
    int retCode = em.createQuery("update FlightOrder o " +
                    "set o.detrRefreshStatus = :detrStatus, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("detrStatus", detrStatus)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "DETR更新(" + detrStatus + ", " + remark + ".");
      em.persist(log);
    }
  }

  @Override
  public int confirmFlightOrderNotice(int enterpriseId, String username, int orderId) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.noticeConfirmed = 1, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      FlightOrder flightOrder = getOrderDetailById(orderId);
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "已读通知(" + flightOrder.getNoticeContent() + ").");

      em.persist(log);
    }

    return retCode;
  }

  @Override
  public List<FlightOrderTicket> searchFlightOrderTickets(int enterpriseId, int orderId) {
    return em.createQuery("select o from FlightOrderTicket o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.order.id = :orderId " +
                    "order by o.id desc", FlightOrderTicket.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("orderId", orderId)
            .getResultList();
  }

  @Override
  public void updateFlightOrderTicketStatus(final int enterpriseId, final String username,
                                            final int orderId, final List<FlightOrderTicket> tickets) {
    FlightOrder flightOrder = em.find(FlightOrder.class, orderId);
    em.refresh(flightOrder);

    if (flightOrder.getDetrRefreshStatus() != 1) {
      throw new RuntimeException("updateFlightOrderTicketStatus failed.");
    }

    flightOrder.setDetrRefreshStatus(2);
    flightOrder.setLastUpdate(DateUtil.getCurDateTime());

    em.merge(flightOrder);

    for (FlightOrderTicket ticket : tickets) {
      if (ticket.getId() == 0) {
        ticket.setOrder(flightOrder);
        em.persist(ticket);
      } else {
        ticket.setLastUpdate(DateUtil.getCurDateTime());
        em.merge(ticket);
      }
    }

  }

  @Override
  public List<FlightOrder> searchFlightOrdersNeedValidate(final int enterpriseId) {
    final Date beginTime = DateUtil.addMinutes(DateUtil.getCurDateTime(), -7);
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.status = :status and o.createTime = :createTime", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", FLIGHT_ORDER_STATUS_FINISHED)
            .setParameter("createTime", beginTime)
            .getResultList();
  }

  @Override
  public List<FlightOrder> searchFlightOrdersByPnrNo(final int enterpriseId, final String pnrNo) {
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.pnrNo = :pnrNo and o.status <> :status order by o.id desc", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", FLIGHT_ORDER_STATUS_CANCELED)
            .setParameter("pnrNo", pnrNo)
            .setMaxResults(10)
            .getResultList();
  }

  @Override
  public void updateFlightOrderNotice(int enterpriseId, String username, int orderId, int noticeType, String noticeContent) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.noticeType = :noticeType, o.noticeContent = :noticeContent, o.noticeConfirmed = null, o.noticeTime = :noticeTime " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId ")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("noticeType", noticeType)
            .setParameter("noticeContent", noticeContent)
            .setParameter("noticeTime", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      String content = "订单通知: " + noticeContent;


      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, content);
      em.persist(log);
    }

  }

  @Override
  public List<FlightOrder> searchFlightOrdersNeedRecheckShowStatus(int enterpriseId, int max) {
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.status = :status " +
                    "and o.showStatus is null order by o.id desc", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", FLIGHT_ORDER_STATUS_FINISHED)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public void updateFlightOrderShowStatus(int enterpriseId, String username, int orderId, int showStatus) {
    em.createQuery("update FlightOrder o set " +
                    "o.showStatus = :newStatus, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.showStatus is null")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("newStatus", showStatus)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
  }

  @Override
  public int saveFlightOrderAttachment(int enterpriseId, String username, int orderId, int orderTyp, List<OrderAttachment> attachments) {
    return saveOrderAttachment(enterpriseId, username, orderTyp, orderId, attachments);
  }

  @Override
  public List<OrderAttachment> searchAttachments(int enterpriseId, int orderId) {
    return searchAttachments(enterpriseId, ORDER_TYPE_FLIGHT, orderId);
  }

  @Override
  public void updateFlightOrderNotifyStatus(int enterpriseId, String username, int id, String content) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.notifyStatus = 1, " +
                    "o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", id)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode > 0) {
      OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_FLIGHT_ORDER);
      log.setOperator(username);
      log.setOrderId(id);
      log.setContent("发送通知：" + content);
      em.persist(log);
    }
  }

  @Override
  public List<FlightOrderFlight> searchFlightOrderFlightsAirportNameIsNull(int enterpriseId) {
    final Date startDate = DateUtil.addDays(DateUtil.getCurDate(), -10);
    return em.createQuery("select o from FlightOrderFlight o " +
                            "where o.enterpriseId = :enterpriseId and o.createTime > :startDate " +
                            "and (o.flight.departureAirportName is null or o.flight.arrivalAirportName is null) order by o.id desc",
                    FlightOrderFlight.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("startDate", startDate)
            .setMaxResults(100)
            .getResultList();
  }

  @Override
  public void updateFlightOrderFlightAirportName(int orderFlightId, int portType, String portName) {
    if (portType == 0) {
      //出发
      em.createQuery("update FlightOrderFlight o set o.flight.departureAirportName = :name " +
                      "where o.id = :id and o.flight.departureAirportName is null")
              .setParameter("id", orderFlightId)
              .setParameter("name", portName)
              .executeUpdate();
    } else {
      //到达
      em.createQuery("update FlightOrderFlight o set o.flight.arrivalAirportName = :name " +
                      "where o.id = :id and o.flight.arrivalAirportName is null")
              .setParameter("id", orderFlightId)
              .setParameter("name", portName)
              .executeUpdate();
    }
  }

  @Override
  public int savePassengers(int enterpriseId, String username, int orderId, List<FlightOrderPassenger> passengers) {
    FlightOrder flightOrder = this.getOrderDetailById(orderId);
    //订单中已经有了乘机人，则不再允许增加
    if (!flightOrder.getPassengers().isEmpty()) {
      return -1;
    }

    int adultCount = 0;
    int childCount = 0;
    int infantCount = 0;

    for (FlightOrderPassenger psg : passengers) {
      psg.setOrder(flightOrder);
      psg.setEnterpriseId(enterpriseId);

      if (psg.getPsgType() == PSG_TYPE_ADU) {
        adultCount++;
      } else if (psg.getPsgType() == PSG_TYPE_CHD) {
        childCount++;
      } else if (psg.getPsgType() == PSG_TYPE_INF) {
        infantCount++;
      }

      em.persist(psg);
    }


    em.createQuery("update FlightOrder o set " +
                    "o.psgCount = :psgCount, " +
                    "o.adultCount = :adultCount, " +
                    "o.childCount = :childCount, " +
                    "o.infantCount = :infantCount, " +
                    "o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("psgCount", passengers.size())
            .setParameter("adultCount", adultCount)
            .setParameter("childCount", childCount)
            .setParameter("infantCount", infantCount)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    var log = createFlightOrderLog(enterpriseId, orderId, username, "上传" + passengers.size() + "个乘机人");
    em.persist(log);

    return passengers.size();
  }

  @Override
  public int updateExternalOrderNo(int enterpriseId, String username, int orderId, String externalOrderNo, String bigPnrNo, String remark) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.externalOrderNo = :orderNo, o.bigPnrNo = :bigPnrNo, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.externalOrderNo is null")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("orderNo", externalOrderNo)
            .setParameter("bigPnrNo", bigPnrNo)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update flightOrder's externalOrderNo.");
    }
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int resetExternalOrderNo(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.externalOrderNo = null, o.bigPnrNo = null, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to reset flightOrder's externalOrderNo.");
    }
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int updateFlightOrderProperty(int enterpriseId, String username, int orderId, final String propertyName, final String propertyValue) {
    String remark = null;
    String hql = null;
    if ("linkman".equalsIgnoreCase(propertyName)) {
      remark = "修改联系人：" + propertyValue;
      hql = "update FlightOrder o set o.linkman = :value,";
    } else if ("linkPhone".equalsIgnoreCase(propertyName)) {
      remark = "修改联系电话: " + propertyValue;
      hql = "update FlightOrder o set o.linkPhone = :value,";
    } else if ("linkMobile".equalsIgnoreCase(propertyName)) {
      remark = "修改联系手机: " + propertyValue;
      hql = "update FlightOrder o set o.linkMobile = :value,";
    } else if ("linkEmail".equalsIgnoreCase(propertyName)) {
      remark = "修改联系Email: " + propertyValue;
      hql = "update FlightOrder o set o.linkEmail = :value,";
    } else if ("bigPnrNo".equalsIgnoreCase(propertyName)) {
      remark = "修改大编码: " + propertyValue;
      hql = "update FlightOrder o set o.bigPnrNo = :value,";
    } else if ("remark".equalsIgnoreCase(propertyName)) {
      remark = "修改备注：" + propertyValue;
      hql = "update FlightOrder o set o.remark = :value,";
    } else {
      return 0;
    }

    hql += " o.lastUpdate = :lastUpdate where o.id = :id and o.enterpriseId = :enterpriseId and o.status <> :status";

    int retCode = em.createQuery(hql)
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .setParameter("value", propertyValue)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int saveExternalOrder(int enterpriseId, String orderNo, int supplierId, String externalOrderNo) {
    var o = new FlightOrderExternal();
    o.setEnterpriseId(enterpriseId);
    o.setSupplierId(supplierId);
    var supplier = em.find(Supplier.class, supplierId);
    o.setSupplierName(supplier.getName());
    o.setOrderNo(orderNo);
    o.setExternalOrderNo(externalOrderNo);

    em.persist(o);
    em.flush();

    return o.getId();

  }

  @Override
  public List<FlightOrderExternal> searchExternalFlightOrders(int enterpriseId, String username, int orderId) {
    var flightOrder = em.find(FlightOrder.class, orderId);
    if (flightOrder == null) {
      logger.info("Cannot find flight order(" + orderId + ").");
      return new ArrayList<>();
    }
    var orderNo = flightOrder.getOrderNo();
    var list = em.createQuery("select o from FlightOrderExternal o " +
            "where o.enterpriseId = :enterpriseId " +
            "and o.orderNo = :orderNo order by o.id desc", FlightOrderExternal.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("orderNo", orderNo)
            .getResultList();
    for(var item: list) {
      item.setSupplierName(em.find(Supplier.class, item.getSupplierId()).getName());
    }

    return list;
  }

  @Override
  public FlightOrderExternal getExternalOrder(int externalOrderId) {
    var o = em.find(FlightOrderExternal.class, externalOrderId);
    if (o != null) {
      em.refresh(o);
    }

    return o;
  }

  @Override
  public int removeExternalOrder(int enterpriseId, String username, int externalOrderId, String remark) {
    return em.createQuery("delete from FlightOrderExternal o " +
            "where o.enterpriseId = :enterpriseId " +
            "and o.id = :id")
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("id", externalOrderId)
            .executeUpdate();
  }

  @Override
  public int updateExternalOrder(int enterpriseId, String username, int externalOrderId, FlightOrderExternal n) {
    var o = em.find(FlightOrderExternal.class, externalOrderId);
    o.setParvalue(n.getParvalue());
    o.setPrice(n.getPrice());
    o.setTax(n.getTax());
    o.setServiceCharge(n.getServiceCharge());
    o.setDiscount(n.getDiscount());
    o.setOrderStatus(n.getOrderStatus());
    o.setRemark(n.getRemark());
    em.merge(o);
    em.flush();
    return o.getId();
  }

  /**
   * 取消机票订单
   */
  @Override
  public int cancelFlightOrder(int enterpriseId, String username, int orderId, int reasonCode,
                               String reasonDesc) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.status = :newStatus " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and (o.status = :oldStatus1 or o.status = :oldStatus2)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus1", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING)
            .setParameter("oldStatus2", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED)
            .executeUpdate();

    if (retCode == 1) {
      String content = "取消订单: " + reasonDesc;
      if (reasonCode > 0) {
        content += "(" + reasonCode + ")";
      }

      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, content);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int submitFlightOrder(final int enterpriseId, final String username, final int orderId) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.status = :newStatus, o.submitTime = :submitTime, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_SUBMITTED)
            .setParameter("submitTime", DateUtil.getCurDateTime())
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to submit flight order.");
    }

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "申请开票，待审批");
    em.persist(log);

    boolean needBeAudited = false;
    var flightOrder = em.find(FlightOrder.class, orderId);
    if (flightOrder.getCustomerType() == 1) {
      //企业客户
      needBeAudited = flightOrder.getCustomer().getApprovalTicket() == 1;
    }

    if (!needBeAudited) {
      // 无需审核
      retCode = em.createQuery("update FlightOrder o set " +
                      "o.status = :newStatus " +
                      "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
              .setParameter("id", orderId)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_SUBMITTED)
              .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
              .executeUpdate();
      if (retCode == 1) {
        log = createFlightOrderLog(enterpriseId, orderId, username, "审批通过，允许开票");
        em.persist(log);

        retCode = FLIGHT_ORDER_STATUS_WAIT_TICKETING; // 全部成功，返回等待开票状态
      }
    }

    return retCode;
  }

  @Override
  public int approveFlightOrder(int enterpriseId, String username, int orderId) {
    int retCode = em
            .createQuery("update FlightOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_SUBMITTED)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "审批通过，允许开票");
      em.persist(log);
      retCode = 2;
    }

    return retCode;
  }

  /**
   * 查询订单日志
   */
  @Override
  public List<OperationLog> searchHistory(int enterpriseId, int orderId) {
    return searchOrderHistory(enterpriseId, OPERATING_OBJECT_FLIGHT_ORDER, orderId);
  }

  @Override
  public void appendFlightOrderLog(int enterpriseId, String username, int orderId, String remark) {
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

  }

  /**
   * 认领开票
   *
   * @param enterpriseId 企业id
   * @param username     用户名
   * @param orderId      订单id
   */
  @Override
  public int toTicketFlightOrder(final int enterpriseId, final String username, final int orderId) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.status = :newStatus, " +
                            "o.ticketer = :ticketer, " +
                            "o.lastUpdate = :lastUpdate " +
                            "where o.id = :id " +
                            "and o.enterpriseId = :enterpriseId " +
                            "and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("ticketer", username)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to claim ticketing.");
    }

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "认领开票");
    em.persist(log);

    return retCode;
  }

  @Override
  public int cancelClaimFlightOrder(int enterpriseId, String username, int orderId) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.status = :newStatus, " +
                            "o.ticketer = null, " +
                            "o.lastUpdate = :lastUpdate " +
                            "where o.id = :id " +
                            "and o.enterpriseId = :enterpriseId " +
                            "and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to cancel claim ticketing.");
    }

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "取消当前开票认领");
    em.persist(log);

    return retCode;
  }

  /**
   * 认领订单开票，并设置是否自动开票
   *
   * @param enterpriseId  企业id
   * @param username      用户名
   * @param orderId       订单id
   * @param autoTicketing 是否自动开票
   */
  @Override
  public int claimTicketingFlightOrder(final int enterpriseId, final String username,
                                       final int orderId, final int autoTicketing) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.status = :newStatus, " +
                            "o.ticketer = :ticketer, " +
                            "o.autoTicketing = :autoTicketing, " +
                            "o.lastUpdate = :lastUpdate " +
                            "where o.id = :id " +
                            "and o.enterpriseId = :enterpriseId " +
                            "and o.status = :oldStatus " +
                            "and o.ticketer is null")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("ticketer", username)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("autoTicketing", autoTicketing)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to claim ticketing.");
    }

    String remark = "认领开票";
    if (autoTicketing == AUTO_PRINT_TICKET_WAITING) {
      remark += ", 自动开票";
    } else {
      remark += ", 非自动开票";
    }
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int finishFlightOrder(int enterpriseId, String username, int orderId, Date etdzDate) {
    int retCode = 0;

    if (etdzDate == null) {
      retCode = em.createQuery(
                      "update FlightOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
                              "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
              .setParameter("id", orderId)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
              .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
              .setParameter("lastUpdate", DateUtil.getCurDateTime())
              .executeUpdate();
    } else {
      retCode = em.createQuery(
                      "update FlightOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate, o.etdzDate = :etdzDate "
                              +
                              "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
              .setParameter("id", orderId)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
              .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
              .setParameter("lastUpdate", DateUtil.getCurDateTime())
              .setParameter("etdzDate", etdzDate)
              .executeUpdate();
    }

    if (retCode == 1) {
      String remark = "订单完成";
      if (etdzDate != null) {
        remark += "(出票日期修改为：" + DateUtil.formatDate(etdzDate, "yyyy-MM-dd") + ").";
      }
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);

      em.persist(log);

    }

    return retCode;
  }

  @Override
  public int updateSupplier(int enterpriseId, String username, int orderId, Supplier supplier,
                            PaymentMethod pm) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.supplier = :supplier, o.paymentMethod = :pm, o.lastUpdate = :lastUpdate "
                            +
                            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status <> :status")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("supplier", supplier)
            .setParameter("pm", pm)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update flight order's supplier & payment method.");
    }

    String remark = "更改供应商为：" + supplier.getName() + ", 支出方式为: ";
    if (pm == null) {
      remark += "null.";
    } else {
      remark += pm.getName() + ".";
    }
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int updateSupplier(int enterpriseId, String username, int orderId, int supplierId) {
    var supplier = em.find(Supplier.class, supplierId);
    if (supplier == null) {
      throw new RuntimeException("Supplier(" + supplierId + ") is not existed.");
    }
    int retCode = em.createQuery(
                    "update FlightOrder o set o.supplier = :supplier, o.lastUpdate = :lastUpdate "
                            +
                            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status <> :status")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("supplier", supplier)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update flight order's supplier method.");
    }

    String remark = "更改供应商为：" + supplier.getName();
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int updateTicketOptions(int enterpriseId, String username, int orderId, Supplier supplier,
                                 PaymentMethod pm, int autoTicketing, int price) {
    int retCode = em
            .createQuery("update FlightOrder o set o.supplier = :supplier, o.paymentMethod = :pm, " +
                    "o.autoTicketing = :autoTicketing, o.autoTicketingPrice = :price,  o.lastUpdate = :lastUpdate "
                    +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status <> :status")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("supplier", supplier)
            .setParameter("pm", pm)
            .setParameter("autoTicketing", autoTicketing)
            .setParameter("price", price)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("It failed to update flight order's supplier & payment method.");
    }

    String remark = "更改供应商为：" + supplier.getName() + ", 支出方式为: ";
    if (pm == null) {
      remark += "null.";
    } else {
      remark += pm.getName() + ".";
    }
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    remark = autoTicketing == 1 ? "自动出票" : "非自动出票";
    if (price > 0) {
      remark += ", 出票价格：" + price;
    }

    log = createFlightOrderLog(enterpriseId, orderId, username, remark);
    em.persist(log);

    return retCode;
  }

  @Override
  public int updatePaymentMethod(int enterpriseId, String username, int orderId,
                                 PaymentMethod paymentMethod) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.paymentMethod = :paymentMethod, o.lastUpdate = :lastUpdate " +
                            "where o.id = :id and o.enterpriseId = :enterpriseId and o.status <> :status")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("paymentMethod", paymentMethod)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username,
              "支付方式更改为：" + paymentMethod.getName());
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int updateDeliveryDate(int enterpriseId, String username, int orderId, Date deliveryDate) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.deliveryDate = :ddate, o.lastUpdate = :lastUpdate " +
                            "where o.id = :id and o.enterpriseId = :enterpriseId ")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("ddate", deliveryDate)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username,
              "派送日期更改为：" + DateUtil.formatDate(deliveryDate, "yyyy-MM-dd"));
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int updateTicketer(int enterpriseId, String username, int orderId, String ticketer) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.ticketer = :ticketer, o.lastUpdate = :lastUpdate " +
                            "where o.id = :id and o.enterpriseId = :enterpriseId ")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("ticketer", ticketer)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username,
              "出票人更改为：" + ticketer);
      em.persist(log);
    }

    return retCode;
  }

  private OperationLog createFlightOrderLog(int enterpriseId, int orderId, String username,
                                            String remark) {
    var log = OperationLogFactory.createFlightOrderOperationLog();
    log.setEnterpriseId(enterpriseId);
    log.setOrderId(orderId);
    log.setOperator(username);
    log.setContent(remark);
    return log;
  }

  @Override
  public int updateTicketNo(final int enterpriseId, final String username, final int orderId,
                            List<FlightOrderPassenger> passengers) {
    int retCode = 0;

    //校验每个乘机人是否都有票号
    for (FlightOrderPassenger psg : passengers) {
      if (psg.getTicketNo().length() < 13) {
        throw new RuntimeException("Passenger(" + psg.getName() + ")'s ticket(" + psg.getTicketNo() + ") is invalid.");
      }
    }

    StringBuilder remark = new StringBuilder("回填票号: ");

    for (FlightOrderPassenger psg : passengers) {
      retCode += em.createQuery("update FlightOrderPassenger o set" +
                      " o.ticketNo = :ticketNo, o.ticketNoEnd = :ticketNoEnd, o.ticketCount = :ticketCount " +
                      " where o.id = :id and o.enterpriseId = :enterpriseId ")
              .setParameter("id", psg.getId())
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("ticketNo", psg.getTicketNo())
              .setParameter("ticketNoEnd", psg.getTicketNoEnd())
              .setParameter("ticketCount", psg.getTicketCount())
              .executeUpdate();
      remark.append(psg.getTicketNo()).append("(").append(psg.getName()).append("),");
    }
    if (retCode != passengers.size()) {
      throw new RuntimeException("Updated FltPsg size is not equal.");
    }

    retCode = em.createQuery("update FlightOrder o set " +
                    "o.status = :newStatus, o.lastUpdate = :lastUpdate, o.etdzDate = :etdzDate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and (o.status = :oldStatus1 or o.status = :oldStatus2)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus1", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("oldStatus2", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("etdzDate", DateUtil.getCurDate())
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("update flight order failed.");
    }

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark.toString());
    em.persist(log);

    return retCode;
  }

  @Override
  public int delFlightOrderExtra(int enterpriseId, String username, int orderId, int extraId) {
    int retCode = 0;

    FlightOrder flightOrder = em.find(FlightOrder.class, orderId);
    if (flightOrder != null) {
      em.refresh(flightOrder);
    }

    if (flightOrder == null || flightOrder.getEnterpriseId() != enterpriseId) {
      em.getTransaction().rollback();
      retCode = -1;
    } else if (flightOrder.getStatus() == 4 || flightOrder.getStatus() == 16) {
      em.getTransaction().rollback();
      retCode = -2;
    } else {

      retCode = em
              .createQuery("delete from FlightOrderExtra o where o.enterpriseId = :enterpriseId " +
                      "and o.order.id = :orderId and o.id = :id")
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("orderId", orderId)
              .setParameter("id", extraId)
              .executeUpdate();


    }

    return retCode;
  }

  @Override
  public int createCancelRequest(int enterpriseId, String username, int orderId, int reasonCode,
                                 String reasonDesc) {
    FlightOrderCancelRequest o = new FlightOrderCancelRequest();
    o.setEnterpriseId(enterpriseId);
    o.setOrderId(orderId);
    o.setOperator(username);
    o.setReasonCode(reasonCode);
    o.setReasonDesc(reasonDesc);

    em.persist(o);

    return o.getId();
  }

  @Override
  public int agreeCancelRequest(int enterpriseId, String username, int orderId, int requestId) {
    int retCode = 0;

    retCode = em.createQuery("update FlightOrder o set " +
                    "o.status = :newStatus " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("to updating flightOrder's status failed");
    }

    retCode = em.createQuery("update FlightOrderCancelRequest o set " +
                    "o.status = :newStatus " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", requestId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", 0)
            .setParameter("newStatus", 1)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("to updating flightOrderCancelRequest's status failed");
    }

    String content = "取消订单: 同意取消请求（" + requestId + ")";
    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, content);

    em.persist(log);

    return retCode;
  }

  @Override
  public void agreeCancelRequest(final int enterpriseId, final String username, final int requestId, final String remark) {
    FlightOrderCancelRequest cr = em.find(FlightOrderCancelRequest.class, requestId);
    final int orderId = cr.getOrderId();
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.status = :newStatus " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("to updating flightOrder's status failed");
    }

    retCode = em.createQuery("update FlightOrderCancelRequest o set " +
                    "o.status = :newStatus " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", requestId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", 0)
            .setParameter("newStatus", 1)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("to updating flightOrderCancelRequest's status failed");
    }

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark + "(" + requestId + ")");
    em.persist(log);

  }

  @Override
  public int denyCancelRequest(int enterpriseId, String username, int orderId, int requestId,
                               int reasonCode, String reasonDesc) {
    int retCode = 0;

    retCode = em.createQuery("update FlightOrderCancelRequest o set " +
                    "o.status = :newStatus, o.denyCode = :denyCode, o.denyDesc = :denyDesc " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", requestId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", 0)
            .setParameter("newStatus", 2)
            .setParameter("denyCode", reasonCode)
            .setParameter("denyDesc", reasonDesc)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("to updating flightOrderCancelRequest's status failed");
    }

    return retCode;
  }

  @Override
  public void denyCancelRequest(int enterpriseId, String username, int requestId, String remark) {
    int retCode = em.createQuery("update FlightOrderCancelRequest o " +
                    "set o.status = :newStatus, o.denyDesc = :denyDesc " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
            .setParameter("id", requestId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("oldStatus", 0)
            .setParameter("newStatus", 2)
            .setParameter("denyDesc", remark)
            .executeUpdate();
    if (retCode != 1) {
      throw new RuntimeException("to updating flightOrderCancelRequest's status failed");
    }

  }

  /**
   * 设置行程单状态为已邮寄
   */
  @Override
  public int finishItineraryDelivery(int enterpriseId, String username, int orderId,
                                     String remark) {
    int retCode = 0;

    retCode = em.createQuery("update FlightOrder o set " +
                    "o.itineraryMailingStatus = 1 " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "行程单已邮寄：" + remark);

      em.persist(log);
    }

    return retCode;
  }

  /**
   * 机票订单已销账
   */
  @Override
  public void setFlightOrderWriteOff(int enterpriseId, String username, int orderId, String remark) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.payStatus = 2 " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId and (o.payStatus is null or o.payStatus <> 2)")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .executeUpdate();

    if (retCode == 1) {
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "已销账：" + remark);

      em.persist(log);
    }

  }

  @Override
  public int incFlightOrderPrintCount(int enterpriseId, String username, int orderId) {
    int retCode;

    FlightOrder order = em.find(FlightOrder.class, orderId);
    if (order == null || order.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("No order info.");
    }

    order.setItineraryPrintCount(order.getItineraryPrintCount() + 1);
    em.merge(order);

    OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, "打印");
    em.persist(log);

    retCode = 1;

    return retCode;
  }

  /**
   * 处于"暂不开票"状态的机票订单可以修改客户
   */
  @Override
  public int updateFlightOrderCustomer(int enterpriseId, String username, int orderId,
                                       int customerId, String costCenter) {
    int retCode;

    String remark;

    if (customerId == 0) {
      //散客
      remark = "客户修改为：散客";
      retCode = em.createQuery(
                      "update FlightOrder o set o.customerType = 0, o.customer = null, o.costCenter = null" +
                              " where o.enterpriseId = :enterpriseId and o.id = :id and o.status = :status")
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("id", orderId)
              .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING)
              .executeUpdate();
    } else {
      Customer customer = em.find(Customer.class, customerId);
      remark = "客户修改为：" + customer.getVipName();
      if (costCenter.length() > 0) {
        remark += ", 成本中心：" + costCenter;
      }

      retCode = em.createQuery(
                      "update FlightOrder o set o.customerType = 1, o.customer.id = :customerId, o.costCenter = :costCenter"
                              +
                              " where o.enterpriseId = :enterpriseId and o.id = :id and o.status = :status")
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("id", orderId)
              .setParameter("customerId", customerId)
              .setParameter("costCenter", costCenter)
              .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING)
              .executeUpdate();
    }

    if (retCode == 1) {
      //记录日志
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

    retCode = 1;

    return retCode;
  }

  @Override
  public int updateFlightOrderIntlTicket(int enterpriseId, String username, int orderId,
                                         int intlTicket) {
    int retCode;

    retCode = em.createQuery(
                    "update FlightOrder o set o.intlTicket = :intlTicket, o.lastUpdate = :lastUpdate " +
                            " where o.enterpriseId = :enterpriseId and o.id = :id and o.status = :status")
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("intlTicket", intlTicket)
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING)
            .executeUpdate();

    if (retCode == 1) {
      //记录日志
      String remark = "订单类型修改为：";
      remark += intlTicket == 1 ? "国际" : "国内";
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

    retCode = 1;

    return retCode;
  }

  @Override
  public int updateFlightOrderKeyCustomerNo(int enterpriseId, String username, int orderId, String keyCustomerNo) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.keyCustomerNo = :keyCustomerNo, o.lastUpdate = :lastUpdate " +
                            " where o.enterpriseId = :enterpriseId and o.id = :id")
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("keyCustomerNo", keyCustomerNo)
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .executeUpdate();

    if (retCode == 1) {
      //记录日志
      String remark = "修改大客户代码为：" + keyCustomerNo;
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int updateFlightOrderAutoTicketing(int enterpriseId, String username, int orderId,
                                            int newStatus, String remark) {
    int retCode = em.createQuery(
                    "update FlightOrder o set o.autoTicketing = :autoTicketing, o.lastUpdate = :lastUpdate " +
                            " where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .setParameter("autoTicketing", newStatus)
            .executeUpdate();

    if (retCode == 1) {
      //记录日志
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);
      em.persist(log);
    }

    return retCode;
  }

  @Override
  public int updateFlightOrderBillImported(int enterpriseId, String username, int flightOrderId) {
    return em.createQuery("update FlightOrder o set o.billImported = 1 " +
                    " where o.enterpriseId = :enterpriseId and o.id = :id")
            .setParameter("id", flightOrderId)
            .setParameter("enterpriseId", enterpriseId)
            .executeUpdate();
  }

  @Override
  public FlightOrderStatusStat statFlightMiscOrderStatus(int enterpriseId) {
    FlightOrderStatusStat stat = new FlightOrderStatusStat();

    //机票订单
    List<Object[]> results = em.createQuery("select o.status, count(o) from FlightOrder o "
                    + " where o.enterpriseId = :enterpriseId"
                    + " group by o.status", Object[].class)
            .setParameter("enterpriseId", enterpriseId)
            .getResultList();

    int status;
    int count;
    for (Object[] result : results) {
      status = ((Number) result[0]).intValue();
      count = ((Number) result[1]).intValue();
      if (status == TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING) {
        stat.setFlightOrderWaitTicketingCount(count);
      } else if (status == TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED) {
        stat.setFlightOrderTicketingCount(count);
      }
    }

    //改签单
    results = em.createQuery("select o.status, count(o) from FlightChangeOrder o "
                    + " where o.enterpriseId = :enterpriseId"
                    + " group by o.status", Object[].class)
            .setParameter("enterpriseId", enterpriseId)
            .getResultList();
    for (Object[] result : results) {
      status = ((Number) result[0]).intValue();
      count = ((Number) result[1]).intValue();
      if (status == FlightChangeOrder.ORDER_STATUS_WAITING) {
        stat.setChangeOrderWaitingCount(count);
      } else if (status == FlightChangeOrder.ORDER_STATUS_WORKING) {
        stat.setChangeOrderProcessingCount(count);
      }
    }

    //退票单
    results = em.createQuery("select o.status, count(o) from FlightRefundOrder o "
                    + " where o.enterpriseId = :enterpriseId"
                    + " group by o.status", Object[].class)
            .setParameter("enterpriseId", enterpriseId)
            .getResultList();

    for (Object[] result : results) {
      status = ((Number) result[0]).intValue();
      count = ((Number) result[1]).intValue();
      if (status == FlightRefundOrder.ORDER_STATUS_WAITING) {
        stat.setRefundOrderWaitingCount(count);
      } else if (status == FlightRefundOrder.ORDER_STATUS_WORKING ||
              status == FlightRefundOrder.ORDER_STATUS_SUBMITTED ||
              status == FlightRefundOrder.ORDER_STATUS_AIR_REFUNDED) {
        stat.setRefundOrderProcessingCount(count + stat.getRefundOrderProcessingCount());
      }
    }

    // k位服务单
    results = em.createQuery(
                    "select o.status, count(o) from VasOrder o  " +
                            "where o.enterpriseId = :enterpriseId and o.productCode = :productCode " +
                            "group by o.status", Object[].class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("productCode", "99001")
            .getResultList();
    for (Object[] result : results) {
      status = ((Number) result[0]).intValue();
      count = ((Number) result[1]).intValue();
      if (status == TmsOrderConstant.ORDER_STATUS_WAITING) {
        stat.setVasOrderKseatWaitingCount(count);
      } else if (status == TmsOrderConstant.ORDER_STATUS_PROCESSING) {
        stat.setVasOrderKseatProcessingCount(count);
      }
    }

    return stat;
  }

  @Override
  public FlightOrder getOrderDetailByOrderNo(int enterpriseId, String orderNo) {
    List<FlightOrder> list = em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId and o.orderNo = :orderNo", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("orderNo", orderNo)
            .getResultList();
    if (list.isEmpty()) {
      return null;
    }

    FlightOrder order = list.get(0);

    order.getPassengers().size();
    order.getFlights().size();
    order.getExtras().size();

    FlightOrderCancelRequest cancelRequest = doGetFlightOrderCancelRequest(em, order.getId());
    order.setCancelRequest(cancelRequest);

    return order;
  }

  @Override
  public List<FlightOrder> searchNoKeyCustomerNoOrders(final int enterpriseId, final int max) {
    final Date startDate = DateUtil.getCurDate();
    return em.createQuery("select o from FlightOrder o " +
                            "where o.enterpriseId = :enterpriseId " +
                            " and o.keyCustomerNo is null " +
                            " and o.createDate = :startDate " +
                            " order by o.id desc",
                    FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("startDate", startDate)
            .setMaxResults(max)
            .getResultList();
  }

  /**
   * 查找未销账的机票订单
   */
  @Override
  public List<FlightOrder> searchUnpaidFlightOrders(final int max) {
    return em.createQuery(
                    "select o from FlightOrder o where o.status = :status " +
                            "and o.createDate >= :createDate " +
                            "and (o.payStatus is null or o.payStatus = 0 or o.payStatus = 1)  " +
                            "order by o.id desc", FlightOrder.class)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .setParameter("createDate", getMinBeginDate())
            .setMaxResults(max)
            .getResultList();
  }

  /**
   * 查找账单导入状态未成功的，已完成的当日订单
   */
  @Override
  public List<FlightOrder> searchUnImportedFlightOrders(int max) {
    return em.createQuery("select o from FlightOrder o " +
                    "where o.createDate = :createDate " +
                    "and o.status = :status " +
                    "and o.billImported is null order by o.id desc", FlightOrder.class)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .setParameter("createDate", DateUtil.getCurDate())
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public void updateFlightOrderPassengerVipAndId(int id, int passengerId, int vipLevel) {
    em.createQuery(
                    "update FlightOrderPassenger o set o.passengerId = :passengerId, o.vipLevel = :vipLevel " +
                            "where o.id = :id")
            .setParameter("id", id)
            .setParameter("passengerId", passengerId)
            .setParameter("vipLevel", vipLevel)
            .executeUpdate();
  }

  @Override
  public List<BlanketOrder> searchSubOrders(int enterpriseId, int blanketOrderId) {
    return em.createQuery("select o from BlanketOrder  o" +
                    " where o.enterpriseId = :enterpriseId" +
                    " and (o.parentId = :parentId)", BlanketOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("parentId", blanketOrderId)
            .getResultList();
  }

  @Override
  public BlanketOrder searchParentOrder(int enterpriseId, int blanketOrderId) {
    BlanketOrder blanketOrder = em.find(BlanketOrder.class, blanketOrderId);
    if (blanketOrder.getParentId() != null) {
      return em.find(BlanketOrder.class, blanketOrder.getParentId());
    } else {
      return null;
    }
  }

  @Override
  public List<FlightOrder> searchNeedPriceCheckFlightOrders(int enterpriseId, Date startDate) {
    return em.createQuery("select o from FlightOrder o "
                    + "where o.enterpriseId = :enterpriseId " +
                    "and (o.status = :status1 or o.status = :status2 or o.status = :status3) " +
                    "and o.createDate >= :createDate " +
                    "and (o.priceChecked is null or o.priceChecked = 0) " +
                    "order by o.id", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status1", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAITING)
            .setParameter("status2", TmsOrderConstant.FLIGHT_ORDER_STATUS_SUBMITTED)
            .setParameter("status3", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("createDate", startDate)
            .getResultList();
  }

  @Override
  public List<FlightOrder> searchNeedAssignApproverFlightOrders(final int enterpriseId) {
    final Date startDate = DateUtil.getYesterday();
    return em.createQuery("select o from FlightOrder o "
                    + "where o.enterpriseId = :enterpriseId " +
                    "and o.status = :status " +
                    "and o.approvalInfo.specifiedApprover is null " +
                    "and o.createDate >= :createDate "
                    + "order by o.id", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_SUBMITTED)
            .setParameter("createDate", startDate)
            .getResultList();
  }

  @Override
  public int updateFlightOrderPriceChecked(int enterpriseId, int orderId, String username, int checkStatus, String remark) {
    int retCode = em.createQuery("update FlightOrder o set o.priceChecked = :checkStatus," +
                    "o.lastUpdate = :lastUpdate, " +
                    "o.priceCheckedTime = :checkTime " +
                    "where o.id = :id")
            .setParameter("id", orderId)
            .setParameter("checkStatus", checkStatus)
            .setParameter("checkTime", DateUtil.getCurDateTime())
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode > 0) {
      OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_FLIGHT_ORDER);
      log.setOperator(username);
      log.setOrderId(orderId);
      log.setContent(remark);
      em.persist(log);
    }
    return retCode;
  }

  @Override
  public int updateFlightOrderApprover(int enterpriseId, String username, int orderId, String approver) {
    int retCode = em.createQuery("update FlightOrder o set o.approvalInfo.specifiedApprover = :approver," +
                    "o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("approver", approver)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode > 0) {
      OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_FLIGHT_ORDER);
      log.setOperator(username);
      log.setOrderId(orderId);
      log.setContent("分派审批人为 " + approver);
      em.persist(log);
    }
    return retCode;
  }

  @Override
  public List<FlightOrder> searchMeetPolicyFlightOrders(int enterpriseId) {
    final Date startDate = DateUtil.getYesterday();
    List<FlightOrder> list = em.createQuery("select o from FlightOrder o "
                    + "where o.enterpriseId = :enterpriseId " +
                    "and o.approvalInfo.violationStatus is null " +
                    "and o.createDate >= :createDate "
                    + "order by o.id", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("createDate", startDate)
            .getResultList();
    for (FlightOrder o : list) {
      o.getPassengers().size();
      o.getFlights().size();
      o.getExtras().size();
    }

    return list;
  }

  @Override
  public void updateFlightOrderViolationStatus(int enterpriseId, String username, int orderId, int status, String remark) {
    int retCode = em.createQuery("update FlightOrder o set " +
                    "o.approvalInfo.violationStatus = :status, " +
                    "o.approvalInfo.violationReason = :reason, " +
                    "o.lastUpdate = :lastUpdate " +
                    "where o.id = :id and o.enterpriseId = :enterpriseId")
            .setParameter("id", orderId)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", status)
            .setParameter("reason", remark)
            .setParameter("lastUpdate", DateUtil.getCurDateTime())
            .executeUpdate();
    if (retCode > 0) {
      OperationLog log = OperationLog.createLog(enterpriseId, OPERATING_OBJECT_FLIGHT_ORDER);
      log.setOperator(username);
      log.setOrderId(orderId);
      String content = status == 0 ? "未违反差旅政策" : "违反了差旅政策";
      content += "(" + remark + ")";
      log.setContent(content);
      em.persist(log);
    }
  }

  @Override
  public List<FlightOrder> searchFlightOrderByStatus(int enterpriseId, int status, int max) {
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.status = :status " +
                    "order by o.id desc", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", status)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public List<FlightOrder> searchFlightOrdersNeedRefreshPnr(int enterpriseId, int max) {
    final Date startDate = DateUtil.getYesterday();
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and o.createDate >= :createDate " +
                    "and o.pnrRefreshStatus is null " +
                    "order by o.id", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("createDate", startDate)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public List<FlightOrder> searchFlightOrdersNeedRefreshDetr(int enterpriseId, int max) {
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and (o.detrRefreshStatus is null or o.detrRefreshStatus = 0) " +
                    "and o.status = :status " +
                    "and o.createDate >= :createDate " +
                    "order by o.id desc", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .setParameter("createDate", DateUtil.addDays(DateUtil.getYesterday(), -365))
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public List<FlightOrder> searchFlightOrdersNeedRecheckDetr(final int enterpriseId, final int max) {
    final Date lastRefreshTime = DateUtil.addMinutes(DateUtil.getCurDateTime(), -120);
    return em.createQuery("select o from FlightOrder o "
                    + "where o.enterpriseId = :enterpriseId " +
                    "and o.status = :status " +
                    "and o.detrRefreshStatus = 2 " +
                    "and o.detrRefreshTime < :lastRefreshTime " +
                    "order by o.id", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
            .setParameter("lastRefreshTime", lastRefreshTime)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public List<FlightOrder> searchFlightOrdersDetrFinished(int enterpriseId, int max) {
    return em.createQuery("select o from FlightOrder o "
                    + "where o.enterpriseId = :enterpriseId " +
                    "and o.detrRefreshStatus = 1 " +
                    " order by o.id desc", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setMaxResults(max)
            .getResultList();
  }

  @Override
  public int rollbackFlightOrderStatus(final int enterpriseId, final String username,
                                       final int orderId, final String remark0) {
    int retCode = 0;

    FlightOrder o = em.find(FlightOrder.class, orderId);
    if (o == null || o.getEnterpriseId() != enterpriseId) {
      throw new RuntimeException("FlightOrder is not existed.");
    }

    if (o.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED) {
      // 已完成 -> 已出票
      retCode = em.createQuery(
                      "update FlightOrder o set o.status = :newStatus, " +
                              "o.autoTicketing = :autoTicketing, " +
                              "o.lastUpdate = :lastUpdate " +
                              "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
              .setParameter("id", orderId)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED)
              .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
              .setParameter("autoTicketing", TmsOrderConstant.AUTO_PRINT_TICKET_NONE)
              .setParameter("lastUpdate", DateUtil.getCurDateTime())
              .executeUpdate();

      if (retCode != 1) {
        throw new RuntimeException("can not update flight order status");
      }

      String remark = "回滚为已出票: " + remark0 + "(非自动出票)。";
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);

      em.persist(log);


    } else if (o.getStatus() == TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED) {
      // 已出票 -> 出票中
      retCode = em.createQuery(
                      "update FlightOrder o set o.status = :newStatus, o.lastUpdate = :lastUpdate " +
                              "where o.id = :id and o.enterpriseId = :enterpriseId and o.status = :oldStatus")
              .setParameter("id", orderId)
              .setParameter("enterpriseId", enterpriseId)
              .setParameter("oldStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
              .setParameter("newStatus", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
              .setParameter("lastUpdate", DateUtil.getCurDateTime())
              .executeUpdate();

      if (retCode != 1) {
        throw new RuntimeException("can not update flight order status");
      }

      String remark = "回滚为出票中: " + remark0;
      OperationLog log = createFlightOrderLog(enterpriseId, orderId, username, remark);

      em.persist(log);


    } else {
      throw new RuntimeException("FlightOrder status can not be rollback.");
    }

    return retCode;
  }


  @Override
  public FlightOrder getOrderDetailById(int id) {
    FlightOrder order = em.find(FlightOrder.class, id);
    if (order == null) {
      return null;
    }

    em.refresh(order);
    order.getPassengers().size();
    order.getFlights().size();
    order.getExtras().size();

    FlightOrderCancelRequest cancelRequest = doGetFlightOrderCancelRequest(em, id);
    order.setCancelRequest(cancelRequest);

    order.setTickets(doGetFlightOrderTickets(em, id));

    return order;
  }

  private FlightOrderCancelRequest doGetFlightOrderCancelRequest(EntityManager em, int orderId) {
    List<FlightOrderCancelRequest> requests = em.createQuery(
                    "select o from FlightOrderCancelRequest o where o.orderId = :orderId order by o.id desc",
                    FlightOrderCancelRequest.class)
            .setParameter("orderId", orderId)
            .setMaxResults(1)
            .getResultList();

    return requests.isEmpty() ? null : requests.get(0);
  }

  private List<FlightOrderTicket> doGetFlightOrderTickets(EntityManager em, int orderId) {
    return em.createQuery("select o from FlightOrderTicket o " +
                    "where o.order.id = :orderId " +
                    "order by o.ddate, o.dtime", FlightOrderTicket.class)
            .setParameter("orderId", orderId)
            .getResultList();
  }

  private List<Predicate> buildPredicates_searchOrder(CriteriaBuilder cb, CriteriaQuery<FlightOrder> cq, Root<FlightOrder> root,
                                                      int enterpriseId, String username,
                                                      SearchFlightOrderCriteria sc, boolean isTicketer, List<Integer> allowedCustomerIds) {

    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(BaseOrderInfo_.enterpriseId), enterpriseId);
    predicates.add(predicate);

    if (isTicketer) {
      // 出票员: 只能查看等待出票的，或者出票中的
      predicate = cb.equal(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING);
      predicate = cb.or(predicate, cb.equal(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED));
      predicates.add(predicate);
    }

    if (sc.getBeginDate() == null) {
      //默认只能查2年内的订单
      predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.createDate), getMinBeginDate());
    } else {
      predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.createDate), sc.getBeginDate());
    }
    predicates.add(predicate);

    if (sc.getEndDate() != null) {
      predicate = cb.lessThanOrEqualTo(root.get(FlightOrder_.createDate), sc.getEndDate());
      predicates.add(predicate);
    }

    if (sc.getStatus() > -1) {
      predicate = cb.equal(root.get(FlightOrder_.status), sc.getStatus());
      predicates.add(predicate);
    }

    if (sc.getCustomerId() == 0) {
      predicate = cb.equal(root.get(BaseOrderInfo_.customerType), 0);
      predicates.add(predicate);
    } else if (sc.getCustomerId() > 0) {
      predicate = cb.equal(root.get(BaseOrderInfo_.customer), sc.getCustomerId());
      predicates.add(predicate);
    }

    if (sc.getCustomerType() == 0) {
      // 散客（企业）
      List<Integer> customerIds = em
              .createQuery("select o.id from Customer o where o.enterpriseType = 0", Integer.class).getResultList();
      predicate = cb.in(root.get(BaseOrderInfo_.customer).get("id")).value(customerIds);
      predicates.add(predicate);
    } else if (sc.getCustomerType() == 1) {
      List<Integer> customerIds = em.createQuery(
                      "select o.id from Customer o where o.enterpriseType = 1 or o.enterpriseType is null", Integer.class)
              .getResultList();
      // 企业
      predicate = cb.in(root.get(BaseOrderInfo_.customer).get("id")).value(customerIds);
      predicates.add(predicate);
    }

    if (sc.getIntlTicket() >= 0) {
      predicate = cb.equal(root.get(FlightOrder_.intlTicket), sc.getIntlTicket());
      predicates.add(predicate);
    }

    if (sc.getLinkman().length() > 0) {
      predicates.add(cb.like(root.get(FlightOrder_.linkman), "%" + sc.getLinkman() + "%"));
    }

    if (sc.getLinkPhone().length() > 0) {
      predicates.add(cb.like(root.get(FlightOrder_.linkPhone), "%" + sc.getLinkPhone() + "%"));
    }

    if (sc.getPnrNo() != null && sc.getPnrNo().trim().length() > 4) {
      predicates.add(cb.equal(root.get(FlightOrder_.pnrNo), sc.getPnrNo()));
    }

    if (sc.getWorkStatus() == 1) {
      //只查看需要处理的订单
      predicate = cb.notEqual(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED);
      predicate = cb.and(predicate,
              cb.notEqual(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED));
      predicate = cb.and(predicate,
              cb.notEqual(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED));
      predicates.add(predicate);
    }

    if (sc.getOnlyMine() == 1) {
      //只看预订人是自己
      predicate = cb.equal(root.get(BaseOrderInfo_.operator), username);
      predicates.add(predicate);
    }

    if (sc.getOp1().length() > 0) {
      //只看指定预订员
      predicate = cb.equal(root.get(BaseOrderInfo_.operator), sc.getOp1());
      predicates.add(predicate);
    }

    if (sc.getOrderNo().length() > 0) {
      //订单号
      predicate = cb.equal(root.get(FlightOrder_.orderNo), sc.getOrderNo());
      predicates.add(predicate);
    } else if (sc.getStatus() == -1) {
      //未指定订单号、订单状态，则不显示已取消的订单
      predicate = cb.notEqual(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_CANCELED);
      predicates.add(predicate);
    }

    if (sc.getPnrNo() != null && sc.getPnrNo().trim().length() > 0) {
      //编码
      predicate = cb.equal(root.get(FlightOrder_.pnrNo), sc.getPnrNo().trim());
      predicates.add(predicate);
    }

    if (sc.getPayStatus() == 0) {
      predicate = cb.isNull(root.get(FlightOrder_.payStatus));
      predicate = cb.or(predicate, cb.equal(root.get(FlightOrder_.payStatus), 0));
      predicates.add(predicate);
    } else if (sc.getPayStatus() == 1) {
      predicate = cb.equal(root.get(FlightOrder_.payStatus), 1);
      predicate = cb.or(predicate, cb.equal(root.get(FlightOrder_.payStatus), 2));
      predicates.add(predicate);
    }

    Subquery<FlightOrderPassenger> subQuery = null;

    boolean b = sc.getName() != null && sc.getName().trim().length() > 0;
    b = b || (sc.getIdno() != null && sc.getIdno().trim().length() > 0);
    b = b || sc.getTicketNo().length() > 0;
    b = b || sc.getLinkman().length() > 0;
    b = b || sc.getLinkPhone().length() > 0;
    b = b || sc.getPhone().length() > 0;

    if (b) {
      subQuery = cq.subquery(FlightOrderPassenger.class);
      Root<FlightOrderPassenger> subRoot = subQuery.from(FlightOrderPassenger.class);
      subQuery.select(subRoot);
      subQuery.alias("s");

      List<Predicate> subQueryPredicates = new ArrayList<>();
      subQueryPredicates
              .add(cb.equal(root.get(BaseOrderInfo_.id), subRoot.get(FlightOrderPassenger_.order)));

      if (sc.getName() != null && sc.getName().trim().length() > 0) {
        subQueryPredicates
                .add(cb.like(subRoot.get(FlightOrderPassenger_.name), "%" + sc.getName().trim() + "%"));
      }

      if (sc.getPhone().length() > 0) {
        subQueryPredicates
                .add(cb.like(subRoot.get(FlightOrderPassenger_.mobile), "%" + sc.getPhone() + "%"));
      }

      if (sc.getIdno() != null && sc.getIdno().trim().length() > 0) {
        subQueryPredicates
                .add(cb.like(subRoot.get(FlightOrderPassenger_.idNo), "%" + sc.getIdno().trim() + "%"));
      }

      if (sc.getTicketNo().length() > 0) {
        subQueryPredicates.add(
                cb.like(subRoot.get(FlightOrderPassenger_.ticketNo), "%" + sc.getTicketNo() + "%"));
      }

      subQuery.where(subQueryPredicates.toArray(new Predicate[]{}));
    }

    if (subQuery != null) {
      predicates.add(cb.exists(subQuery));
    }

    if (sc.getFlightNo().length() > 0 || (sc.getDepartureDate() != null
            && sc.getDepartureDate().length() == 10)) {
      // 航班号，或起飞日期
      Subquery<FlightOrderFlight> subQuery1 = cq.subquery(FlightOrderFlight.class);
      Root<FlightOrderFlight> subRoot = subQuery1.from(FlightOrderFlight.class);
      subQuery1.select(subRoot);
      subQuery1.alias("f");

      List<Predicate> subQueryPredicates = new ArrayList<>();
      subQueryPredicates
              .add(cb.equal(root.get(BaseOrderInfo_.id), subRoot.get(FlightOrderFlight_.order)));

      if (sc.getFlightNo().length() > 0) {
        subQueryPredicates.add(
                cb.equal(subRoot.get(FlightOrderFlight_.flight).get("flightNo"), sc.getFlightNo()));
      }

      if (sc.getDepartureDate() != null && sc.getDepartureDate().length() == 10) {
        subQueryPredicates.add(cb.equal(subRoot.get(FlightOrderFlight_.flight).get("departureDate"),
                sc.getDepartureDate()));
      }

      subQuery1.where(subQueryPredicates.toArray(new Predicate[]{}));
      predicates.add(cb.exists(subQuery1));
    }

    if (allowedCustomerIds != null && !allowedCustomerIds.isEmpty()) {
      // 只允许查看指定的企业客户的订单
      predicate = cb.in(root.get(BaseOrderInfo_.customer).get("id")).value(allowedCustomerIds);
      predicates.add(predicate);
    }

    return predicates;
  }

  @Override
  public List<FlightOrder> searchOrder(int enterpriseId, String username,
                                       SearchFlightOrderCriteria sc, boolean isTicketer, List<Integer> allowedCustomerIds) {
    List<FlightOrder> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<FlightOrder> cq = cb.createQuery(FlightOrder.class);
    Root<FlightOrder> root = cq.from(FlightOrder.class);
    root.alias("o");

    var predicates0 = buildPredicates_searchOrder(cb, cq, root, enterpriseId, username, sc, isTicketer, allowedCustomerIds);

    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<FlightOrder> countRoot = countQuery.from(FlightOrder.class);
    countRoot.alias("o");

    var predicates1 = buildPredicates_searchOrder(cb, cq, countRoot, enterpriseId, username, sc, isTicketer, allowedCustomerIds);

    // count items
    countQuery.select(cb.count(countRoot))
            .where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.desc(root.get("id")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      sc.getPage().setRowCount(maxResults);

      results = em.createQuery(cq)
              .setFirstResult(sc.getPage().getFirstPosition())
              .setMaxResults(sc.getPage().getPageSize())
              .getResultList();

      for (FlightOrder order : results) {
        order.getFlights().size();
        order.getPassengers().size();
        order.getExtras().size();
      }

    }
    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  @Override
  public List<FlightOrder> searchOrder(CommonSearchCriteria sc, Date beginDate) {
    List<FlightOrder> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<FlightOrder> cq = cb.createQuery(FlightOrder.class);
    Root<FlightOrder> root = cq.from(FlightOrder.class);
    root.alias("o");

    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    //已完成的订单
    predicate = cb.equal(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED);
    predicates.add(predicate);

    //指定时间以来的订单
    predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.createDate), beginDate);
    predicates.add(predicate);

    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<FlightOrder> countRoot = countQuery.from(FlightOrder.class);
    countRoot.alias("o");

    // count items
    countQuery.select(cb.count(countRoot))
            .where(predicates.toArray(new Predicate[predicates.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates.toArray(new Predicate[predicates.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.asc(root.get("id")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      sc.getPage().setRowCount(maxResults);

      results = em.createQuery(cq)
              .setFirstResult(sc.getPage().getFirstPosition())
              .setMaxResults(sc.getPage().getPageSize())
              .getResultList();

      for (FlightOrder order : results) {
        order.getPassengers().size();
      }

    }
    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  /**
   * 搜索当天的，待认领和已认领出票的订单
   *
   * @param enterpriseId 企业id
   * @param startDate    最早订单生成日期
   */
  @Override
  public List<FlightOrder> searchWaitingForAutoTicketOrder(final int enterpriseId, final Date startDate) {
    return em.createQuery("select o from FlightOrder o " +
                    "where o.enterpriseId = :enterpriseId " +
                    "and (o.status = :status1 or o.status = :status2 or o.status = :status3) " +
                    "and o.createDate >= :createDate " +
                    "order by o.id", FlightOrder.class)
            .setParameter("enterpriseId", enterpriseId)
            .setParameter("status1", TmsOrderConstant.FLIGHT_ORDER_STATUS_WAIT_TICKETING)
            .setParameter("status2", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETING_ACCEPTED)
            .setParameter("status3", TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED)
            .setParameter("createDate", startDate)
            .getResultList();
  }

  private List<Predicate> buildPredicates_searchNeedDeliveryFlightOrders(CriteriaBuilder cb, Root<FlightOrder> root,
                                                                         int enterpriseId,
                                                                         SearchFlightOrderCriteria sc) {
    List<Predicate> predicates = new ArrayList<>();

    Predicate predicate = cb.conjunction();
    predicates.add(predicate);

    predicate = cb.equal(root.get(BaseOrderInfo_.enterpriseId), enterpriseId);
    predicates.add(predicate);

    if (sc.getSearchType() == SearchFlightOrderCriteria.SEARCH_TO_PRINT) {
      // 待打印订单
      predicate = cb.equal(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_TICKETED);
      predicate = cb.or(predicate,
              cb.equal(root.get(FlightOrder_.status), TmsOrderConstant.FLIGHT_ORDER_STATUS_FINISHED));
      predicates.add(predicate);
    }

    if (sc.getSearchType() == SearchFlightOrderCriteria.SEARCH_TO_DELIVERY) {
      //只显示未派送的
      predicate = cb.isNull(root.get(FlightOrder_.itineraryMailingStatus));
      predicate = cb.or(predicate, cb.equal(root.get(FlightOrder_.itineraryMailingStatus), 0));
      predicates.add(predicate);

      //派送日期
      if (sc.getBeginDate() != null && sc.getEndDate() != null) {
        predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.deliveryDate), sc.getBeginDate());
        predicate = cb.and(predicate,
                cb.lessThanOrEqualTo(root.get(FlightOrder_.deliveryDate), sc.getEndDate()));
        predicates.add(predicate);
      } else if (sc.getBeginDate() != null) {
        predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.deliveryDate), sc.getBeginDate());
        predicates.add(predicate);
      } else if (sc.getEndDate() != null) {
        predicate = cb.lessThanOrEqualTo(root.get(FlightOrder_.deliveryDate), sc.getEndDate());
        predicates.add(predicate);
      }
    } else if (sc.getSearchType() == SearchFlightOrderCriteria.SEARCH_TO_PRINT) {
      if (sc.getBeginDate() != null && sc.getEndDate() != null) {
        predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.etdzDate), sc.getBeginDate());
        predicate = cb
                .and(predicate, cb.lessThanOrEqualTo(root.get(FlightOrder_.etdzDate), sc.getEndDate()));
        predicates.add(predicate);
      } else if (sc.getBeginDate() != null) {
        predicate = cb.greaterThanOrEqualTo(root.get(FlightOrder_.etdzDate), sc.getBeginDate());
        predicates.add(predicate);
      } else if (sc.getEndDate() != null) {
        predicate = cb.lessThanOrEqualTo(root.get(FlightOrder_.etdzDate), sc.getEndDate());
        predicates.add(predicate);
      }
    }

    //行程单类型
    if (sc.getItineraryType() > 0) {
      predicate = cb.equal(root.get(FlightOrder_.itineraryType), sc.getItineraryType()); // 需要行程单或发票
      predicates.add(predicate);
    } else {
      // 所有需要派送的
      predicate = cb.gt(root.get(FlightOrder_.itineraryType), 0); // 需要行程单或发票
      predicates.add(predicate);
    }

    //未打印
    if (sc.getPrintCount() == 0) {
      predicate = cb.equal(root.get(FlightOrder_.itineraryPrintCount), sc.getPrintCount()); // 未打印
      predicate = cb.or(predicate, cb.isNull(root.get(FlightOrder_.itineraryPrintCount)));
      predicates.add(predicate);
    }

    //国际国内
    if (sc.getIntlTicket() == 0) {
      //国内
      predicate = cb.equal(root.get(FlightOrder_.intlTicket), 0);
      predicate = cb.or(predicate, cb.isNull(root.get(FlightOrder_.intlTicket)));
      predicates.add(predicate);
    } else if (sc.getIntlTicket() == 1) {
      //国际
      predicate = cb.equal(root.get(FlightOrder_.intlTicket), 1);
      predicates.add(predicate);
    }

    if (sc.getCustomerId() == 0) {
      // 散客
      predicate = cb.equal(root.get(BaseOrderInfo_.customerType), 0);
      predicates.add(predicate);
    } else if (sc.getCustomerId() > 0) {
      // 指定客户
      predicate = cb.equal(root.get(BaseOrderInfo_.customer), sc.getCustomerId());
      predicates.add(predicate);
    }

    return predicates;
  }

  @Override
  public List<FlightOrder> searchNeedDeliveryFlightOrders(int enterpriseId,
                                                          SearchFlightOrderCriteria sc) {
    List<FlightOrder> results = null;

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<FlightOrder> cq = cb.createQuery(FlightOrder.class);
    Root<FlightOrder> root = cq.from(FlightOrder.class);
    root.alias("o");

    var predicates0 = buildPredicates_searchNeedDeliveryFlightOrders(cb, root, enterpriseId, sc);

    //计算根据条件查询得出的数据总数
    CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
    Root<FlightOrder> countRoot = countQuery.from(FlightOrder.class);
    countRoot.alias("o");

    var predicates1 = buildPredicates_searchNeedDeliveryFlightOrders(cb, countRoot, enterpriseId, sc);

    // count items
    countQuery.select(cb.count(countRoot))
            .where(predicates1.toArray(new Predicate[predicates1.size()]));
    Long count = em.createQuery(countQuery).getSingleResult();

    //查询
    cq.select(root).where(predicates0.toArray(new Predicate[predicates0.size()]));

    List<Order> orderByes = new ArrayList<>();
    orderByes.add(cb.desc(root.get("id")));

    cq.orderBy(orderByes);

    int maxResults = count.intValue();
    if (maxResults > 0) {
      sc.getPage().setRowCount(maxResults);

      results = em.createQuery(cq)
              .setFirstResult(sc.getPage().getFirstPosition())
              .setMaxResults(sc.getPage().getPageSize())
              .getResultList();

      for (FlightOrder order : results) {
        order.getFlights().size();
        order.getPassengers().size();
        order.getExtras().size();
      }
    }

    if (results == null) {
      results = new ArrayList<>();
    }

    return results;
  }

  @Override
  public List<FlightOrderFlight> searchFlightOrderFlights(int flightOrderId) {
    return em.createQuery(
                    "select o from FlightOrderFlight o where o.order.id = :orderId order by o.segOrder",
                    FlightOrderFlight.class)
            .setParameter("orderId", flightOrderId)
            .getResultList();
  }

  @Override
  public List<FlightOrderPassenger> searchFlightOrderPassengers(String name, int idType, String idNo) {
    return em.createQuery("select o from FlightOrderPassenger o " +
                            "where o.name = :name " +
                            "and o.idType = :idType " +
                            "and o.idNo = :idNo " +
                            "and o.mobile <> '' order by o.id desc",
                    FlightOrderPassenger.class)
            .setParameter("name", name)
            .setParameter("idType", idType)
            .setParameter("idNo", idNo)
            .setMaxResults(5)
            .getResultList();
  }

}
